#include "lom-instance-manager.hh"
#include "lom-errors.hh"
#include "lom-root.hh"
#include "lom-instance.hh"
#include <cassert>

using namespace boost;
using namespace licada;
using namespace std;

namespace lom
{
    LomInstanceManager::LomInstanceManager() : curHandle( 1 ) {}

    LomInstanceManager::~LomInstanceManager()
    {
	reset();
    }
    static inline void remover( const std::pair< THandle, LomRoot* >& item, THandleRootMap& rootMap )
    {
	THandleRootMap::iterator find = rootMap.find( item.first);
	//Screw the references, we are simple removing these objects.
	//Because nothing references them directly (everything just
	//gets a handle) this is safer than it might appear.
	if ( find != rootMap.end() ) {
	    LomRoot* root = find->second;
	    do_times( root->getRefCount(), bind( &LomRefCounted::removeRef, root ) );
	}
    }

    void LomInstanceManager::reset() { internalReset(); }
    
    void LomInstanceManager::internalReset()
    {
	THandleRootMap tmp = handleToRoots;
	std::for_each( tmp.begin(), tmp.end(), boost::bind( remover, _1, ref(handleToRoots) ));
	assert( handleToRoots.size() == 0 );
	curHandle = 1;
	reservedHandles.clear();
    }
    
    LomRoot* LomInstanceManager::getRoot( THandle handle, bool throwFail )
    {
	THandleRootMap::iterator find = handleToRoots.find( handle );
	if ( find == handleToRoots.end() )
	{
	    if ( throwFail )
		throw InstanceNotFound( handle );
	    return NULL;
	}
	return find->second;
    }
    
    LomInstance* LomInstanceManager::getInstance( THandle handle, bool throwFail )
    {
	LomRoot* root = getRoot( handle, throwFail );
	LomInstance* retval = dynamic_cast< LomInstance* >( root );
	if ( retval == NULL && throwFail )
	    throw InstanceNotFound( handle );
	return retval;
    }
    
    THandle LomInstanceManager::doregister( LomRoot* item, THandle reservedHandle )
    {
	THandle registerHandle = reservedHandle;
	if ( reservedHandle == 0 ) {
	    registerHandle = curHandle;
	    ++curHandle;
	}
	else
	{
	    THandleUInt32Map::iterator find = reservedHandles.find( reservedHandle );
	    if ( find == reservedHandles.end() )
		throw ReservedHandleDoesNotExist();
	    do_times( find->second, bind( &LomRefCounted::addRef, item ) );
	    reservedHandles.erase( find );
	}
	bool success = handleToRoots.insert( std::make_pair(registerHandle, item ) ).second;
	if ( !success )
	    throw HandleExists();
	sendInstanceCreated( registerHandle );
	return registerHandle;
    }
    
    void LomInstanceManager::deregister( THandle handle )
    {
	THandleRootMap::iterator find = handleToRoots.find( handle );
	if ( find != handleToRoots.end() )
	    handleToRoots.erase( find );
    }
    
    void LomInstanceManager::reference( THandle handle, bool errorIfNotExist )
    {
	THandleUInt32Map::iterator find = reservedHandles.find( handle );
	if ( find != reservedHandles.end() ) {
	    ++find->second;
	    return;
	}
	LomRoot* item = getRoot( handle, errorIfNotExist );
	if ( item )
	    item->addRef();
    }
    
    void LomInstanceManager::dereference( THandle handle )
    {
	THandleUInt32Map::iterator find = reservedHandles.find( handle );
	if ( find != reservedHandles.end() && find->second > 0) {
	    --find->second;
	    return;
	}
	LomRoot* item = getRoot( handle, false );
	if ( item )
	    item->removeRef();
    }
    
    THandle LomInstanceManager::reserveHandle() {
	THandle reserve = curHandle;
	++curHandle;
	reservedHandles.insert( std::make_pair( reserve, 0 ) );
	return reserve;
    }
    
    const THandleRootMap& LomInstanceManager::getHandleToRoots()
    {
	return handleToRoots;
    }
    
    
}
